#pragma once

#include <glibmm/refptr.h>
#include <glibmm/ustring.h>
#include <glibmm/thread.h>
#include <assert.h>

namespace BuildNG
{
  class WeakPtrStub;
  class WeakPtrBase;

  class ObjectBase
  {
    friend class WeakPtrBase;

  public:
    ObjectBase();
    virtual ~ObjectBase();

    void reference();
    void unreference();

    template<class T>
    Glib::RefPtr<T> this_ptr()
    {
      reference();

      T *casted_ptr = dynamic_cast<T*>(this);
      assert(casted_ptr);

      return Glib::RefPtr<T>(casted_ptr);
    }

    template<class T>
    Glib::RefPtr<T> this_ptr() const 
    {
      return const_cast<ObjectBase*>(this)->this_ptr<T>();
    }

  private:
    int           ref_count;
    WeakPtrStub*  weakptr_stub;
  };

  class Object : public virtual ObjectBase
  {
  };

  class Interface : public virtual ObjectBase
  {
  };

  class WeakPtrStub
  {
  public:  
    WeakPtrStub()
    {
      stub_refcount = 1;
    }

    ObjectBase*   obj;
    Glib::Mutex   obj_mutex;
    int           stub_refcount;
  };

  class WeakPtrBase
  {
  public:
    WeakPtrBase();
    WeakPtrBase(const WeakPtrBase &src);
    ~WeakPtrBase();

    Glib::RefPtr<ObjectBase> acquire();

  protected:
    void assign(const Glib::RefPtr<ObjectBase> &src);
    void assign(const WeakPtrBase &src);
    void clear();

  private:
    WeakPtrStub*    obj_stub;
  };

  template <class T>
  class WeakPtr : public WeakPtrBase
  {
  public:
    WeakPtr(){}

    template <class OtherT>
    WeakPtr(const Glib::RefPtr<OtherT> &src)
    {
      // just to trigger a compiler error when they are not compatible
      T *ptr = (OtherT*)0;

      assign(src);
    }

    WeakPtr(const WeakPtr<T> &src)
      : WeakPtrBase(src)
    {
    }

    template <class OtherT>
    WeakPtr(const WeakPtr<OtherT> &src)
      : WeakPtrBase(src)
    {
      // just to trigger a compiler error when they are not compatible
      T *ptr = (OtherT*)0;
    }

    template <class OtherT>
    WeakPtr<T>& operator = (const Glib::RefPtr<OtherT> &src)
    {
      // just to trigger a compiler error when they are not compatible
      T *ptr = (OtherT*)0;

      assign(src);
      return *this;
    }

    WeakPtr<T>& operator = (const WeakPtr<T> &src)
    {
      assign(src);
      return *this;
    }

    template <class OtherT>
    WeakPtr<T>& operator = (const WeakPtr<OtherT> &src)
    {
      // just to trigger a compiler error when they are not compatible
      T *ptr = (OtherT*)0;

      assign(src);
      return *this;
    }

    void clear()
    {
      WeakPtrBase::clear();
    }

    Glib::RefPtr<T> acquire()
    {
      Glib::RefPtr<ObjectBase> base_ptr = WeakPtrBase::acquire();
      Glib::RefPtr<T> ret = Glib::RefPtr<T>::cast_dynamic(base_ptr);
      assert(ret || !base_ptr);
      return ret;
    }
  };
}
